package myutil.concurrency;

/**
 *
 * @author Hlib_Babii
 */
public class Series {

    private final int n;
    private final int nThreads;

    private final int numbersPerProcess;
    private final int numbersLeft;
    

    /**
     * 
     * @param n last number of the series
     * @param nThreads number of threads for calculation
     */
    public Series(int n, int nThreads) {
        this.n = n;
        this.nThreads = nThreads;
        
        numbersPerProcess = (2 * n + 1) / nThreads;
        numbersLeft = (2 * n + 1) % nThreads;
    }
    
    private class TaskThread extends Thread {
        
        private int num = 0;
        /**
         * sum counted by this thread
         */
        private double threadSum = 0.0;
        
        public TaskThread(int num) {
            this.num = num;
        }

        @Override
        public void run() {
            calcSubSum();
        }
        
        public double getThreadSum() {
            return threadSum;
        }
        
        public int getNum() {
            return num;
        }        

        private void calcSubSum() {
            for (int j = 0; j < numbersPerProcess; j++) {
                int arg = -n + num * numbersPerProcess + j;
                threadSum += Math.sin(arg);
            }
            if (num < numbersLeft) {
                threadSum += Math.sin(n - num);
            }
        }
    }

    /**
     * returns the sum of the series
     * @return the sum of the series 
     */
    public double seriesSum() {
        double sum = 0.0;
        TaskThread[] t = new TaskThread[nThreads];
        for (int i = 0; i < nThreads; i++) {
            t[i] = new TaskThread(i);
            t[i].start();
        }
        // wait until all threads are completed
        for (int i = 0; i < nThreads; i++) {
            try {
                t[i].join();
            } catch (InterruptedException ex) {}
        }
        for (int i = 0; i < nThreads; i++) {
            sum += t[i].getThreadSum();
        }
        return sum;
    }

    public static void main(String[] args) {
        int failCounter = 0;
        for (int i = 0; i < 1000; i++) {
            Series s = new Series(5000, 10);
            double result = s.seriesSum();
            if (Math.abs(result) > 1e-6) {
                ++failCounter;
                System.out.println("wrong sum= " + result);
            }
            System.out.println("sum= " + result);
        }
        System.out.println("failCounter= " + failCounter);
    }
}
